﻿using System;
using System.Collections.Concurrent;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;

namespace 新门禁.Utils
{
    /// <summary>
    /// 单路海康车牌识别会话：
    /// Start(previewWnd): 登录 + 布防（新报警）+ 可选预览
    /// Stop(): 停止预览 + 撤防 + 注销
    /// 通过 PlateDetected 事件拿到车牌（已去掉颜色前缀）
    /// </summary>
    public sealed class HikLprSession : IDisposable
    {
        // ===== 静态：SDK 初始化 & 全局报警回调（多设备共用一次） =====
        private static bool _sdkInited = false;
        private static CHCNetSDK.MSGCallBack_V31 _globalAlarmCb;
        private static readonly ConcurrentDictionary<int, HikLprSession> _sessionsByUserId =
            new ConcurrentDictionary<int, HikLprSession>();

        private static void EnsureSdk()
        {
            if (_sdkInited) return;

            if (!CHCNetSDK.NET_DVR_Init())
                throw new Exception("NET_DVR_Init failed");

            // （可选优化）设置连接与重连（你的 SDK 声明第二参是 int，就用 1/0）
            CHCNetSDK.NET_DVR_SetConnectTime(2000, 1);
            CHCNetSDK.NET_DVR_SetReconnect(10000, 1); // 1=启用重连，0=禁用

            _globalAlarmCb = new CHCNetSDK.MSGCallBack_V31(GlobalAlarmCallback); //全局报警回调：把不同设备的报警分发到各自会话
            CHCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(_globalAlarmCb, IntPtr.Zero);//注册全局回调

            _sdkInited = true;
        }

        /// <summary>
        /// 全局报警回调：把不同设备的报警分发到各自会话
        /// </summary>
        private static bool GlobalAlarmCallback(int lCommand, ref CHCNetSDK.NET_DVR_ALARMER pAlarmer,
                                                IntPtr pAlarmInfo, uint dwBufLen, IntPtr pUser)
        {
            try
            {
                HikLprSession target = null;

                // 1) 优先用 lUserID（大多数版本都有）
                int uid = 0;
                try { uid = pAlarmer.lUserID; } catch { uid = 0; }

                if (uid != 0)
                {
                    HikLprSession s1;
                    if (_sessionsByUserId.TryGetValue(uid, out s1))
                        target = s1;
                }

                // 2) 退化用设备IP匹配（必要时）
                if (target == null)
                {
                    string ip = GetAlarmDevIp(ref pAlarmer);
                    foreach (var kv in _sessionsByUserId)
                    {
                        if (kv.Value.DeviceIp == ip) { target = kv.Value; break; }
                    }
                }

                if (target != null)
                    target.HandleAlarm(lCommand, pAlarmInfo, dwBufLen); // ===== 被全局回调调用：解析车牌并触发事件 =====
            }
            catch
            {
                // 忽略异常，保证 SDK 回调不中断
            }
            return true;
        }

        private static string GetAlarmDevIp(ref CHCNetSDK.NET_DVR_ALARMER a)
        {
            try
            {
                byte[] raw = a.sDeviceIP ?? new byte[0];
                string s = Encoding.ASCII.GetString(raw);
                return s.TrimEnd('\0', ' ');
            }
            catch { return string.Empty; }
        }

        // ===== 颜色去除 & 车牌解码 =====
        private static readonly Regex _colorPrefix = new Regex(
            @"^[\s\(\[（【\-]*(黄绿|渐变绿|蓝|黄|白|黑|绿)(牌|色)?[:：]?[\s\)\]）】\-]*",
            RegexOptions.Compiled);

        private static string StripPlateColor(string plate)
        {
            if (string.IsNullOrWhiteSpace(plate)) return string.Empty;
            return _colorPrefix.Replace(plate.Trim(), "");
        }

        private static string ReadPlate(byte[] src)
        {
            if (src == null) return string.Empty;
            int len = Array.IndexOf(src, (byte)0);
            if (len < 0) len = src.Length;
            return Encoding.GetEncoding(936).GetString(src, 0, len).Trim(); // 936=GBK
        }

        // ===== 每路会话字段 =====
        public string Name { get; private set; }
        public string DeviceIp { get; private set; }
        public ushort Port { get; private set; }
        public string Username { get; private set; }
        public string Password { get; private set; }
        public int Channel { get; private set; }

        public IntPtr PreviewWnd { get; private set; }

        private int _userId = -1;//句柄
        private int _alarmHandle = -1;
        private int _realHandle = -1;

        /// <summary>
        /// 车牌事件（plate, session name）
        /// </summary>
        public event Action<string, string> PlateDetected;

        public bool IsStarted { get { return _userId >= 0; } }

        public HikLprSession(string name, string ip, ushort port, string user, string pwd, int channel)
        {
            Name = name;
            DeviceIp = ip;
            Port = port;
            Username = user;
            Password = pwd;
            Channel = channel;
            PreviewWnd = IntPtr.Zero;

            EnsureSdk();
        }

        public enum BarrierCmd : byte { Close = 0, Open = 1, Stop = 2, Lock = 3, Unlock = 4 }

        public bool BarrierGateCtrl(BarrierCmd cmd, uint channel = 1, byte laneNo = 1, byte entranceNo = 1, byte unlockEnable = 0)
        {
            if (_userId < 0) return false;

            var cfg = new CHCNetSDK.NET_DVR_BARRIERGATE_CFG
            {
                dwSize = (uint)Marshal.SizeOf(typeof(CHCNetSDK.NET_DVR_BARRIERGATE_CFG)),
                dwChannel = channel,
                byLaneNo = laneNo,
                byBarrierGateCtrl = (byte)cmd,
                byEntranceNo = entranceNo,
                byUnlock = unlockEnable,
                byRes = new byte[24]
            };

            IntPtr p = IntPtr.Zero;
            try
            {
                int sz = Marshal.SizeOf(cfg);
                p = Marshal.AllocHGlobal(sz);
                Marshal.StructureToPtr(cfg, p, false);

                bool ok = CHCNetSDK.NET_DVR_RemoteControl(_userId, CHCNetSDK.NET_DVR_BARRIERGATE_CTRL, p, sz);
                if (!ok)
                {
                    uint err = CHCNetSDK.NET_DVR_GetLastError();
                    System.Diagnostics.Debug.WriteLine($"BARRIERGATE_CTRL fail cmd={cmd}, err={err}");
                }
                return ok;
            }
            finally
            {
                if (p != IntPtr.Zero) Marshal.FreeHGlobal(p);
            }
        }


        /// <summary>
        /// 控制道闸
        /// </summary>
        /// <param name="alarmOutIndex"></param>
        /// <param name="pulseMs"></param>
        /// <returns></returns>
        public bool PulseAlarmOut(int alarmOutIndex, int pulseMs = 300)
        {
            bool aa=CHCNetSDK.NET_DVR_SetAlarmOut(_userId, 0, 1);

            if (_userId < 0) return false;
            // 置位（导通）
            if (!CHCNetSDK.NET_DVR_SetAlarmOut(_userId, alarmOutIndex, 1)) return false;
            System.Threading.Thread.Sleep(pulseMs);
            // 复位（断开）
            return CHCNetSDK.NET_DVR_SetAlarmOut(_userId, alarmOutIndex, 0);
        }



        /// <summary>
        /// 启动：登录 + 布防（新报警）+ 可选预览（传入 WinForms 控件句柄）
        /// </summary>
        public void Start(IntPtr previewWnd = default(IntPtr))
        {
            if (_userId >= 0) return;

            // 登录
            CHCNetSDK.NET_DVR_USER_LOGIN_INFO loginInfo = new CHCNetSDK.NET_DVR_USER_LOGIN_INFO();
            byte[] byIP = Encoding.Default.GetBytes(DeviceIp);
            loginInfo.sDeviceAddress = new byte[129]; byIP.CopyTo(loginInfo.sDeviceAddress, 0);
            byte[] byUser = Encoding.Default.GetBytes(Username);
            loginInfo.sUserName = new byte[64]; byUser.CopyTo(loginInfo.sUserName, 0);
            byte[] byPwd = Encoding.Default.GetBytes(Password);
            loginInfo.sPassword = new byte[64]; byPwd.CopyTo(loginInfo.sPassword, 0);
            loginInfo.wPort = Port;
            loginInfo.bUseAsynLogin = false;

            CHCNetSDK.NET_DVR_DEVICEINFO_V40 devInfo = new CHCNetSDK.NET_DVR_DEVICEINFO_V40();
            _userId = CHCNetSDK.NET_DVR_Login_V40(ref loginInfo, ref devInfo);
            if (_userId < 0)
            {
                uint err = CHCNetSDK.NET_DVR_GetLastError();
                throw new Exception("Login failed: " + err);
            }

            // 记录 userId -> session，用于回调分发
            _sessionsByUserId[_userId] = this;

            // 布防（新报警：COMM_ITS_PLATE_RESULT）
            CHCNetSDK.NET_DVR_SETUPALARM_PARAM p = new CHCNetSDK.NET_DVR_SETUPALARM_PARAM();
            p.dwSize = (uint)Marshal.SizeOf(typeof(CHCNetSDK.NET_DVR_SETUPALARM_PARAM));
            p.byLevel = 1;          // 二级布防，最多3路
            p.byAlarmInfoType = 1;  // 新报警
            p.byDeployType = 1;     // 布防
            _alarmHandle = CHCNetSDK.NET_DVR_SetupAlarmChan_V41(_userId, ref p);//打开车牌识别结果上传的“总开关
            if (_alarmHandle < 0)
            {
                uint err = CHCNetSDK.NET_DVR_GetLastError();
                throw new Exception("SetupAlarm failed: " + err + "（28=连接上限）");
            }

            // 预览（可选）
            if (previewWnd != IntPtr.Zero)
            {
                PreviewWnd = previewWnd;
                CHCNetSDK.NET_DVR_PREVIEWINFO prev = new CHCNetSDK.NET_DVR_PREVIEWINFO();
                prev.hPlayWnd = previewWnd;
                prev.lChannel = Channel;
                prev.dwStreamType = 0; // 主码流
                prev.dwLinkMode = 0;   // TCP
                prev.bBlocked = true;
                prev.dwDisplayBufNum = 1;
                prev.byProtoType = 0;
                prev.byPreviewMode = 0;

                _realHandle = CHCNetSDK.NET_DVR_RealPlay_V40(_userId, ref prev, null, IntPtr.Zero);
                if (_realHandle < 0)
                {
                    uint err = CHCNetSDK.NET_DVR_GetLastError();
                    throw new Exception("RealPlay failed: " + err);
                }
            }
        }

        /// <summary>
        /// 停止：停止预览 + 撤防 + 注销
        /// </summary>
        public void Stop()
        {
            try
            {
                if (_realHandle >= 0)
                {
                    CHCNetSDK.NET_DVR_StopRealPlay(_realHandle);
                    _realHandle = -1;
                }
            }
            catch { }

            try
            {
                if (_alarmHandle >= 0)
                {
                    CHCNetSDK.NET_DVR_CloseAlarmChan_V30(_alarmHandle);
                    _alarmHandle = -1;
                }
            }
            catch { }

            try
            {
                if (_userId >= 0)
                {
                    CHCNetSDK.NET_DVR_Logout(_userId);
                    HikLprSession _; // 丢弃
                    _sessionsByUserId.TryRemove(_userId, out _);
                    _userId = -1;
                }
            }
            catch { }
        }

        public void Dispose()
        {
            Stop();
        }

        // ===== 被全局回调调用：解析车牌并触发事件 =====
        internal void HandleAlarm(int lCommand, IntPtr pAlarmInfo, uint dwBufLen)
        {
            try
            {
                if (pAlarmInfo == IntPtr.Zero) return;

                if (lCommand == CHCNetSDK.COMM_ITS_PLATE_RESULT)
                {
                    CHCNetSDK.NET_ITS_PLATE_RESULT its =
                        (CHCNetSDK.NET_ITS_PLATE_RESULT)Marshal.PtrToStructure(
                            pAlarmInfo, typeof(CHCNetSDK.NET_ITS_PLATE_RESULT));

                    string raw = ReadPlate(its.struPlateInfo.sLicense);//海康把牌照放在 struPlateInfo.sLicense 这个定长字节数组里（以 \0 结尾）。编码通常是 GB2312/ GBK，所以别用 UTF-8 直接解。
                    string plate = StripPlateColor(raw);

                    Application.Current?.Dispatcher.BeginInvoke(new Action(delegate
                    {
                        System.Diagnostics.Debug.WriteLine("[" + Name + "] Plate=" + plate);
                        Action<string, string> handler = PlateDetected;
                        if (handler != null) handler(plate, Name);
                    }));
                }
                else if (lCommand == CHCNetSDK.COMM_UPLOAD_PLATE_RESULT)
                {
                    CHCNetSDK.NET_DVR_PLATE_RESULT oldr =
                        (CHCNetSDK.NET_DVR_PLATE_RESULT)Marshal.PtrToStructure(
                            pAlarmInfo, typeof(CHCNetSDK.NET_DVR_PLATE_RESULT));

                    string raw = ReadPlate(oldr.struPlateInfo.sLicense);
                    string plate = StripPlateColor(raw);

                    Application.Current?.Dispatcher.BeginInvoke(new Action(delegate
                    {
                        System.Diagnostics.Debug.WriteLine("[" + Name + "] Plate=" + plate);
                        Action<string, string> handler = PlateDetected;
                        if (handler != null) handler(plate, Name);
                    }));
                }
                // 其他报警类型忽略
            }
            catch
            {
                // 吞异常：不影响 SDK 回调链
            }
        }
    }

    /// <summary>
    /// 工厂/辅助：可选
    /// </summary>
    internal static class HikLprUtils
    {
        public static HikLprSession Create(string name, string ip, ushort port, string user, string pwd, int channel)
        {
            return new HikLprSession(name, ip, port, user, pwd, channel);
        }
    }


}
